import Vue from 'vue';
import merge from 'element-ui/src/utils/merge';
import {getKeysMap, getRowIdentity, getColumnById, getColumnByKey, orderBy, toggleRowStatus} from '../util';
import expand from './expand';
import current from './current';
import tree from './tree';

const sortData = (data, states) => {
    const sortingColumn = states.sortingColumn;
    if (!sortingColumn || typeof sortingColumn.sortable === 'string') {
        return data;
    }
    return orderBy(data, states.sortProp, states.sortOrder, sortingColumn.sortMethod, sortingColumn.sortBy);
};

const doFlattenColumns = (columns) => {
    const result = [];
    columns.forEach((column) => {
        if (column.children) {
            result.push.apply(result, doFlattenColumns(column.children));
        } else {
            result.push(column);
        }
    });
    return result;
};

export default Vue.extend({
    data() {
        return {
            states: {
                // 3.0 版本后要求必须设置该属性
                rowKey: null,

                // 渲染的数据来源，是对 table 中的 data 过滤排序后的结果
                data: [],

                // 是否包含固定列
                isComplex: false,

                // 列
                _columns: [], // 不可响应的
                originColumns: [],
                columns: [],
                fixedColumns: [],
                rightFixedColumns: [],
                leafColumns: [],
                fixedLeafColumns: [],
                rightFixedLeafColumns: [],
                leafColumnsLength: 0,
                fixedLeafColumnsLength: 0,
                rightFixedLeafColumnsLength: 0,

                // 选择
                isAllSelected: false,
                selection: [],
                reserveSelection: false,
                selectOnIndeterminate: false,
                selectable: null,

                // 过滤
                filters: {}, // 不可响应的
                filteredData: null,

                // 排序
                sortingColumn: null,
                sortProp: null,
                sortOrder: null,

                hoverRow: null
            }
        };
    },

    mixins: [expand, current, tree],

    methods: {
        // 检查 rowKey 是否存在
        assertRowKey() {
            const rowKey = this.states.rowKey;
            if (!rowKey) throw new Error('[ElTable] prop row-key is required');
        },

        // 更新列
        updateColumns() {
            const states = this.states;
            const _columns = states._columns || [];
            states.fixedColumns = _columns.filter((column) => column.fixed === true || column.fixed === 'left');
            states.rightFixedColumns = _columns.filter((column) => column.fixed === 'right');

            if (states.fixedColumns.length > 0 && _columns[0] && _columns[0].type === 'selection' && !_columns[0].fixed) {
                _columns[0].fixed = true;
                states.fixedColumns.unshift(_columns[0]);
            }

            const notFixedColumns = _columns.filter(column => !column.fixed);
            states.originColumns = [].concat(states.fixedColumns).concat(notFixedColumns).concat(states.rightFixedColumns);

            const leafColumns = doFlattenColumns(notFixedColumns);
            const fixedLeafColumns = doFlattenColumns(states.fixedColumns);
            const rightFixedLeafColumns = doFlattenColumns(states.rightFixedColumns);

            states.leafColumnsLength = leafColumns.length;
            states.fixedLeafColumnsLength = fixedLeafColumns.length;
            states.rightFixedLeafColumnsLength = rightFixedLeafColumns.length;

            states.columns = [].concat(fixedLeafColumns).concat(leafColumns).concat(rightFixedLeafColumns);
            states.isComplex = states.fixedColumns.length > 0 || states.rightFixedColumns.length > 0;
        },

        // 更新 DOM
        scheduleLayout(needUpdateColumns) {
            if (needUpdateColumns) {
                this.updateColumns();
            }
            this.table.debouncedUpdateLayout();
        },

        // 选择
        isSelected(row) {
            const {selection = []} = this.states;
            return selection.indexOf(row) > -1;
        },

        clearSelection() {
            const states = this.states;
            states.isAllSelected = false;
            const oldSelection = states.selection;
            if (oldSelection.length) {
                states.selection = [];
                this.table.$emit('selection-change', []);
            }
        },

        cleanSelection() {
            const states = this.states;
            const {data, rowKey, selection} = states;
            let deleted;
            if (rowKey) {
                deleted = [];
                const selectedMap = getKeysMap(selection, rowKey);
                const dataMap = getKeysMap(data, rowKey);
                for (let key in selectedMap) {
                    if (selectedMap.hasOwnProperty(key) && !dataMap[key]) {
                        deleted.push(selectedMap[key].row);
                    }
                }
            } else {
                deleted = selection.filter(item => data.indexOf(item) === -1);
            }
            if (deleted.length) {
                const newSelection = selection.filter(item => deleted.indexOf(item) === -1);
                states.selection = newSelection;
                this.table.$emit('selection-change', newSelection.slice());
            }
        },

        toggleRowSelection(row, selected, emitChange = true) {
            const changed = toggleRowStatus(this.states.selection, row, selected);
            if (changed) {
                const newSelection = (this.states.selection || []).slice();
                // 调用 API 修改选中值，不触发 select 事件
                if (emitChange) {
                    this.table.$emit('select', newSelection, row);
                }
                this.table.$emit('selection-change', newSelection);
            }
        },

        _toggleAllSelection() {
            const states = this.states;
            const {data = [], selection} = states;
            // when only some rows are selected (but not all), select or deselect all of them
            // depending on the value of selectOnIndeterminate
            const value = states.selectOnIndeterminate
                ? !states.isAllSelected
                : !(states.isAllSelected || selection.length);
            states.isAllSelected = value;

            let selectionChanged = false;
            data.forEach((row, index) => {
                if (states.selectable) {
                    if (states.selectable.call(null, row, index) && toggleRowStatus(selection, row, value)) {
                        selectionChanged = true;
                    }
                } else {
                    if (toggleRowStatus(selection, row, value)) {
                        selectionChanged = true;
                    }
                }
            });

            if (selectionChanged) {
                this.table.$emit('selection-change', selection ? selection.slice() : []);
            }
            this.table.$emit('select-all', selection);
        },

        updateSelectionByRowKey() {
            const states = this.states;
            const {selection, rowKey, data} = states;
            const selectedMap = getKeysMap(selection, rowKey);
            data.forEach(row => {
                const rowId = getRowIdentity(row, rowKey);
                const rowInfo = selectedMap[rowId];
                if (rowInfo) {
                    selection[rowInfo.index] = row;
                }
            });
        },

        updateAllSelected() {
            const states = this.states;
            const {selection, rowKey, selectable} = states;
            // data 为 null 时，解构时的默认值会被忽略
            const data = states.data || [];
            if (data.length === 0) {
                states.isAllSelected = false;
                return;
            }

            let selectedMap;
            if (rowKey) {
                selectedMap = getKeysMap(selection, rowKey);
            }
            const isSelected = function (row) {
                if (selectedMap) {
                    return !!selectedMap[getRowIdentity(row, rowKey)];
                } else {
                    return selection.indexOf(row) !== -1;
                }
            };
            let isAllSelected = true;
            let selectedCount = 0;
            for (let i = 0, j = data.length; i < j; i++) {
                const item = data[i];
                const isRowSelectable = selectable && selectable.call(null, item, i);
                if (!isSelected(item)) {
                    if (!selectable || isRowSelectable) {
                        isAllSelected = false;
                        break;
                    }
                } else {
                    selectedCount++;
                }
            }

            if (selectedCount === 0) isAllSelected = false;
            states.isAllSelected = isAllSelected;
        },

        // 过滤与排序
        updateFilters(columns, values) {
            if (!Array.isArray(columns)) {
                columns = [columns];
            }
            const states = this.states;
            const filters = {};
            columns.forEach(col => {
                states.filters[col.id] = values;
                filters[col.columnKey || col.id] = values;
            });

            return filters;
        },

        updateSort(column, prop, order) {
            if (this.states.sortingColumn && this.states.sortingColumn !== column) {
                this.states.sortingColumn.order = null;
            }
            this.states.sortingColumn = column;
            this.states.sortProp = prop;
            this.states.sortOrder = order;
        },

        execFilter() {
            const states = this.states;
            const {_data, filters} = states;
            let data = _data;

            Object.keys(filters).forEach((columnId) => {
                const values = states.filters[columnId];
                if (!values || values.length === 0) return;
                const column = getColumnById(this.states, columnId);
                if (column && column.filterMethod) {
                    data = data.filter((row) => {
                        return values.some(value => column.filterMethod.call(null, value, row, column));
                    });
                }
            });

            states.filteredData = data;
        },

        execSort() {
            const states = this.states;
            states.data = sortData(states.filteredData, states);
        },

        // 根据 filters 与 sort 去过滤 data
        execQuery(ignore) {
            if (!(ignore && ignore.filter)) {
                this.execFilter();
            }
            this.execSort();
        },

        clearFilter(columnKeys) {
            const states = this.states;
            const {tableHeader, fixedTableHeader, rightFixedTableHeader} = this.table.$refs;

            let panels = {};
            if (tableHeader) panels = merge(panels, tableHeader.filterPanels);
            if (fixedTableHeader) panels = merge(panels, fixedTableHeader.filterPanels);
            if (rightFixedTableHeader) panels = merge(panels, rightFixedTableHeader.filterPanels);

            const keys = Object.keys(panels);
            if (!keys.length) return;

            if (typeof columnKeys === 'string') {
                columnKeys = [columnKeys];
            }

            if (Array.isArray(columnKeys)) {
                const columns = columnKeys.map(key => getColumnByKey(states, key));
                keys.forEach(key => {
                    const column = columns.find(col => col.id === key);
                    if (column) {
                        // TODO: 优化这里的代码
                        panels[key].filteredValue = [];
                    }
                });
                this.commit('filterChange', {
                    column: columns,
                    values: [],
                    silent: true,
                    multi: true
                });
            } else {
                keys.forEach(key => {
                    // TODO: 优化这里的代码
                    panels[key].filteredValue = [];
                });

                states.filters = {};
                this.commit('filterChange', {
                    column: {},
                    values: [],
                    silent: true
                });
            }
        },

        clearSort() {
            const states = this.states;
            if (!states.sortingColumn) return;

            this.updateSort(null, null, null);
            this.commit('changeSortCondition', {
                silent: true
            });
        },

        // 适配层，expand-row-keys 在 Expand 与 TreeTable 中都有使用
        setExpandRowKeysAdapter(val) {
            // 这里会触发额外的计算，但为了兼容性，暂时这么做
            this.setExpandRowKeys(val);
            this.updateTreeExpandKeys(val);
        },

        // 展开行与 TreeTable 都要使用
        toggleRowExpansionAdapter(row, expanded) {
            const hasExpandColumn = this.states.columns.some(({type}) => type === 'expand');
            if (hasExpandColumn) {
                this.toggleRowExpansion(row, expanded);
            } else {
                this.toggleTreeExpansion(row, expanded);
            }
        }
    }
});
